// IMPORTANT: Read the license included with this code archive.

namespace Hosting
{
    using System;
    using System.Collections;
    using System.ComponentModel.Design;
    using System.Windows.Forms;

    public class SelectionService : ISelectionService
    {
        private readonly IDesignerHost host;

        private readonly ArrayList selectedComponents;

        public event EventHandler SelectionChanging;

        public event EventHandler SelectionChanged;

        public SelectionService(IDesignerHost host)
        {
            this.host = host;

            this.selectedComponents = new ArrayList();

            // Subscribe to the componentremoved event
            var c = (IComponentChangeService)host.GetService(typeof(IComponentChangeService));
            c.ComponentRemoved += this.OnComponentRemoved;
        }

        public ICollection GetSelectedComponents()
        {
            return this.selectedComponents.ToArray();
        }

        internal void OnSelectionChanging(EventArgs e)
        {
            // Fire the SelectionChanging event if anything is bound to it
            if (this.SelectionChanging != null)
            {
                this.SelectionChanging(this, e);
            }
        }

        internal void OnSelectionChanged(EventArgs e)
        {
            // Fire the SelectionChanging event if anything is bound to it
            if (this.SelectionChanged != null)
            {
                this.SelectionChanged(this, e);
            }
        }

        public object PrimarySelection
        {
            get
            {
                // If we have any selected components, return the first
                if (this.selectedComponents.Count > 0)
                {
                    return this.selectedComponents[0];
                }

                return null;
            }
        }

        public int SelectionCount
        {
            get
            {
                return this.selectedComponents.Count;
            }
        }

        public bool GetComponentSelected(object component)
        {
            return this.selectedComponents.Contains(component);
        }

        public void SetSelectedComponents(ICollection components, SelectionTypes selectionType)
        {
            bool control = false;
            bool shift = false;

            // Raise selectionchanging event
            if (this.SelectionChanging != null)
            {
                this.SelectionChanging(this, EventArgs.Empty);
            }

            // If we're being passed an empty collection
            if (components == null || components.Count == 0)
            {
                components = new object[1] { this.host.RootComponent };
            }

            // If the selection type is Click, we want to know if shift or control is being held
            if ((selectionType & SelectionTypes.Click) == SelectionTypes.Click)
            {
                control = ((Control.ModifierKeys & Keys.Control) == Keys.Control);
                shift = ((Control.ModifierKeys & Keys.Shift) == Keys.Shift);
            }

            if (selectionType == SelectionTypes.Replace)
            {
                // Simply replace our existing collection with the new one
                this.selectedComponents.Clear();
                foreach (var component in components)
                {
                    if (component != null && !this.selectedComponents.Contains(component))
                    {
                        this.selectedComponents.Add(component);
                    }
                }
            }
            else
            {
                // Clear selection if ctrl or shift isn't pressed
                if (!control && !shift && components.Count == 1)
                {
                    foreach (var component in components)
                    {
                        if (!this.selectedComponents.Contains(component))
                        {
                            this.selectedComponents.Clear();
                        }
                    }
                }

                // Add or remove each component to or from the selection
                foreach (var component in components)
                {
                    if (component != null)
                    {
                        if (control || shift)
                        {
                            if (this.selectedComponents.Contains(component))
                            {
                                this.selectedComponents.Remove(component);
                            }
                            else
                            {
                                this.selectedComponents.Insert(0, component);
                            }
                        }
                        else
                        {
                            if (!this.selectedComponents.Contains(component))
                            {
                                this.selectedComponents.Add(component);
                            }
                            else
                            {
                                this.selectedComponents.Remove(component);
                                this.selectedComponents.Insert(0, component);
                            }
                        }
                    }
                }
            }

            // Raise the selectionchanged event
            if (this.SelectionChanged != null)
            {
                this.SelectionChanged(this, EventArgs.Empty);
            }
        }

        public void SetSelectedComponents(ICollection components)
        {
            // Use the Replace selection type because this needs to replace anything already selected
            this.SetSelectedComponents(components, SelectionTypes.Replace);
        }

        internal void OnComponentRemoved(object sender, ComponentEventArgs e)
        {
            if (this.selectedComponents.Contains(e.Component))
            {
                // Raise the selectionchanging event
                this.OnSelectionChanging(EventArgs.Empty);

                // Remove this component from the selected components
                this.selectedComponents.Remove(e.Component);

                // Select root component if that leaves us with no selected components
                if (this.SelectionCount == 0)
                {
                    this.selectedComponents.Add(this.host.RootComponent);
                }

                // Raise the selectionchanged event
                this.OnSelectionChanged(EventArgs.Empty);
            }
        }
    }
}